package com.zp.manager.uploadtemplate;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.OSS;
import com.zp.configuration.AliyunOssClient;
import com.zp.domain.file.UploadPictureResult;
import com.zp.enums.ErrorCode;
import com.zp.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;

// 手动定义日志，避免 Lombok @Slf4j 注解处理缺失
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 模版方法：兼容本地文件上传与 URL 上传（使用阿里云 OSS）
 */
public abstract class PictureUploadTemplate {
    private static final Logger log = LoggerFactory.getLogger(PictureUploadTemplate.class);

    @Resource
    private AliyunOssClient aliyunOssClient;
    @Resource
    private OSS ossClient;

    /**
     * 文件上传
     *
     * @param t                本地文件 / URL
     * @param uploadPathPrefix 上传路径前缀（例如 space/{spaceId} 或 public）
     * @return 封装图片基本信息的对象
     */
    public <T> UploadPictureResult uploadPicture(T t, String uploadPathPrefix) {
        // 1. 校验图片(抽象方法, 由子类实现)
        validPicture(t);
        // 2. 拼接文件名称
        String date = DateUtil.formatDate(new Date());
        String uuid = RandomUtil.randomString(10);
        // 获取原始文件名, 拼接文件名
        String originalFilename = getOriginalFilename(t);
        String suffix = FileUtil.getSuffix(originalFilename);
        String fileName = String.format("%s_%s.%s", date, uuid, (StrUtil.equals(suffix, "") ? "png" : suffix));
        // 处理外部 URL 可能带有 ? 的情况
        if (fileName.contains("?")) {
            fileName = fileName.split("\\?")[0];
        }
        // 3. 划分文件夹（去掉前导斜杠，避免 OSS 对象键以 "/" 开头）
        String uploadPath = String.format("%s/%s", uploadPathPrefix, fileName);
        // 4. 上传图片到 OSS
        File file = null;
        try {
            // 1. 创建临时文件
            String tempPrefix = "upload_" + RandomUtil.randomString(2);
            file = File.createTempFile(tempPrefix, null);
            // 处理文件, 本地文件: 上传到服务器, URL: 下载文件
            processFile(t, file);
            // 2. 上传图片到 OSS
            String bucket = aliyunOssClient.getBucket();
            ossClient.putObject(bucket, uploadPath, file);
            // 3. 计算图片元信息（本地计算）
            BufferedImage image = ImageIO.read(file);
            if (image == null) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "图片解析失败");
            }
            int width = image.getWidth();
            int height = image.getHeight();
            double scale = NumberUtil.round(width * 1.0 / height, 2).doubleValue();
            String format = StrUtil.isBlank(suffix) ? "png" : suffix.toLowerCase();
            String aveColorHex = computeAverageHexColor(image);
            // 4. 封装返回结果
            UploadPictureResult result = new UploadPictureResult();
            String host = aliyunOssClient.getHost();
            if (StrUtil.isBlank(host)) {
                String endpoint = aliyunOssClient.getEndpoint();
                String bucketName = aliyunOssClient.getBucket();
                host = "https://" + bucketName + "." + endpoint;
            }
            result.setUrl(host + "/" + uploadPath);
            result.setPicName(FileUtil.mainName(originalFilename));
            result.setPicSize(FileUtil.size(file));
            result.setPicWidth(width);
            result.setPicHeight(height);
            result.setPicScale(scale);
            result.setPicFormat(format);
            result.setPicColor(aveColorHex);
            return result;
        } catch (Exception e) {
            log.error("file upload fail", e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "上传失败");
        } finally {
            // 5. 删除临时文件
            deleteTempFile(file);
        }
    }

    /**
     * 计算图片平均颜色（返回 0xRRGGBB 格式）
     */
    private String computeAverageHexColor(BufferedImage image) {
        long rSum = 0, gSum = 0, bSum = 0;
        int width = image.getWidth();
        int height = image.getHeight();
        // 采样步长，避免大图遍历过慢
        int stepX = Math.max(1, width / 200);
        int stepY = Math.max(1, height / 200);
        long count = 0;
        for (int y = 0; y < height; y += stepY) {
            for (int x = 0; x < width; x += stepX) {
                int rgb = image.getRGB(x, y);
                int r = (rgb >> 16) & 0xFF;
                int g = (rgb >> 8) & 0xFF;
                int b = (rgb) & 0xFF;
                rSum += r;
                gSum += g;
                bSum += b;
                count++;
            }
        }
        int rAvg = (int) (rSum / Math.max(1, count));
        int gAvg = (int) (gSum / Math.max(1, count));
        int bAvg = (int) (bSum / Math.max(1, count));
        return String.format("0x%02x%02x%02x", rAvg, gAvg, bAvg);
    }

    /**
     * 校验图片 / 校验 URL
     *
     * @param t   图片 / URL
     * @param <T> 图片 / URL
     */
    protected abstract <T> void validPicture(T t);

    /**
     * 获取原始文件名, 拼接文件名
     *
     * @param t   源文件: 本地文件 / URL
     * @param <T> 源文件类型
     * @return 文件名
     */
    protected abstract <T> String getOriginalFilename(T t);

    /**
     * 处理文件, 本地文件: 上传到服务器, URL: 下载文件
     *
     * @param t   本地文件 /  URL
     * @param <T> 文件上传方式
     */
    protected abstract <T> void processFile(T t, File file) throws IOException;

    /**
     * 删除临时文件
     *
     * @param file 临时文件
     */
    private void deleteTempFile(File file) {
        if (file == null) {
            return;
        }
        boolean deleted = file.delete();
        if (!deleted) {
            log.error("delete file fail");
        }
    }
}
